#include "tray-icon-items-model.h"
#include <QMetaEnum>
#include <QDebug>
#include <QCursor>

#include <config-loader.h>

namespace UkuiSystemTray {
static const QStringList DEFAULT_ORDERED_ITEMS = {"ukui-power-manager-tray",
                                                  "ukui-search",
                            "ukui-volume-control-applet-qt",
                            "kylin-nm",
                            "ukui-bluetooth"};
static const QStringList DEFAULT_FIXED_ITEMS = {"ukui-sidebar"};

GroupModel::GroupModel(QObject *parent) : QSortFilterProxyModel(parent)
{
    setFilterRole(TrayIconItemsModel::Row);
}

void GroupModel::setSourceModel(QAbstractItemModel *sourceModel)
{
    m_source = qobject_cast<TrayIconItemsModel*>(sourceModel);
    QSortFilterProxyModel::setSourceModel(sourceModel);
    connect(sourceModel, &QAbstractItemModel::dataChanged, this, [&](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()){
        Q_EMIT dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight), roles);
    });
}

void GroupModel::setGroupBegin(int groupBegin)
{
    if(m_groupBegin == groupBegin) {
        return;
    }
    m_groupBegin = groupBegin;
    invalidateFilter();
}

int GroupModel::groupBegin() const
{
    return m_groupBegin;
}

void GroupModel::setGroupEnd(int groupEnd)
{
    if(m_groupEnd == groupEnd) {
        return;
    }
    m_groupEnd = groupEnd;
    invalidateFilter();
}

int GroupModel::groupEnd() const
{
    return m_groupEnd;
}

QHash<int, QByteArray> GroupModel::roleNames() const
{
    return m_source->roleNames();
}

QVariant GroupModel::data(const QModelIndex &index, int role) const
{
    return m_source->data(mapToSource(index), role);
}

bool GroupModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
   return source_row >= m_groupBegin && source_row <= m_groupEnd;
}

TrayIconItemsModel::TrayIconItemsModel(QObject *parent) : QAbstractListModel(parent)
{
    if (!m_settings) {
        m_settings = UkuiPanel::ConfigLoader::getGlobalWidgetConfig("org.ukui.systemTray")->settings();
    }
    //fixed items
    if(!m_settings->contains(QStringLiteral("fixedItems"))) {
        m_fixedItems = DEFAULT_FIXED_ITEMS;
        m_settings->setValue(QStringLiteral("fixedItems"), DEFAULT_FIXED_ITEMS);
    } else {
        m_fixedItems = m_settings->value("fixedItems").toStringList();
    }
    //items have orders
    if(!m_settings->contains(QStringLiteral("orderedItems"))) {
        m_order = DEFAULT_ORDERED_ITEMS;
        m_settings->setValue(QStringLiteral("orderedItems"), DEFAULT_ORDERED_ITEMS);
    } else {
        m_order = m_settings->value(QStringLiteral("orderedItems")).toStringList();
    }

    //固定的items放在order items的前面
    m_order = m_fixedItems + m_order;
    connect(UkuiSni::StatusNotifierHost::self(), &UkuiSni::StatusNotifierHost::itemAdded, this, &TrayIconItemsModel::addSource);
    connect(UkuiSni::StatusNotifierHost::self(), &UkuiSni::StatusNotifierHost::itemRemoved, this, &TrayIconItemsModel::removeSource);

    qDebug() << "init===" <<m_order;
    for (const auto& service: UkuiSni::StatusNotifierHost::self()->services()) {
        addSource(service);
    }
}

int TrayIconItemsModel::rowCount(const QModelIndex &parent) const
{
    return m_item.length();
}

QVariant TrayIconItemsModel::data(const QModelIndex &index, int role) const
{
    int i = index.row();
    if ((i < 0) || (i >= m_item.length())) {
        return {};
    }
    auto *item = m_item[i];
    switch (static_cast<Role>(role)) {
        case Role::Service:
            return item->service();
        case Role::AttentionIcon:
            return item->attentionIcon();
        case Role::AttentionIconName:
            return item->attentionIconName();
        case Role::AttentionMovieName:
            return item->attentionMovieName();
        case Role::Category:
            return item->category();
        case Role::Icon:
            return item->icon();
        case Role::IconName:
            return item->iconName();
        case Role::IconThemePath:
            return item->iconThemePath();
        case Role::Id:
            return item->id();
        case Role::ItemIsMenu:
            return item->itemIsMenu();
        case Role::OverlayIcon:
            return item->overlayIcon();
        case Role::OverlayIconName:
            return item->overlayIconName();
        case Role::Status:
            return item->status();
        case Role::Title:
            return item->title();
        case Role::ToolTipSubTitle:
            return item->toolTipSubTitle();
        case Role::ToolTipTitle:
            return item->toolTipTitle();
        case Role::WindowId:
            return item->windowId();
        case Role::Fixed:
            return item->fixed();
        case Role::RecordOrder:
            return item->recordOrder();
        case Role::Row:
            return index.row();
        default:
            return {};
    }
}

QHash<int, QByteArray> TrayIconItemsModel::roleNames() const
{
    QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
    QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role"));

    for (int i = 0; i < e.keyCount(); ++i) {
        roles.insert(e.value(i), e.key(i));
    }
    return roles;
}
QModelIndex TrayIconItemsModel::index(int row, int column, const QModelIndex &parent) const
{
    if (row < 0 || column != 0) {
        return {};
    }
    return createIndex(row, column, nullptr);
}

void TrayIconItemsModel::setOrderInGroup(UkuiSystemTray::GroupModel *group, const QModelIndex &groupIndex, int order)
{
    int end = group->groupEnd();
    int newOrder = group->groupBegin() + order;
    if(newOrder > end) {
        newOrder = end;
    }
    setOrder(group->mapToSource(groupIndex), newOrder);
}

void TrayIconItemsModel::setOrderBetweenGroups(UkuiSystemTray::GroupModel *fromGroup, const QModelIndex &beginIndex,
                                               UkuiSystemTray::GroupModel *toGroup, int order)
{
    int newOrder = toGroup->groupBegin() + order;
    if(newOrder > toGroup->groupEnd()) {
        newOrder = toGroup->groupEnd();
    }
    setOrder(fromGroup->mapToSource(beginIndex), newOrder);
}
void TrayIconItemsModel::setGroupBegin(UkuiSystemTray::GroupModel *group, int begin)
{

}

void TrayIconItemsModel::setGroupEnd(UkuiSystemTray::GroupModel *group, int end)
{

}
int TrayIconItemsModel::indexOfSource(const QString &source) const
{
    for (int i = 0; i < rowCount(QModelIndex()); i++) {
        if (m_item[i]->source() == source) {
            return i;
        }
    }
    return -1;
}

void TrayIconItemsModel::addSource(const QString &source)
{
    auto item = new TrayIconItem(source);

    connect(item, &TrayIconItem::dataUpdated, this, [&, source]() {
        dataUpdated(source);
    });
    connect(item, &TrayIconItem::contextMenuReady, this, &TrayIconItemsModel::showMenu);

    if(item->id().isEmpty()) {
        return;
    }
    int order = m_item.size();
    for(const QString &id : m_order) {
        if(id == item->id()) {
            int index = m_order.indexOf(id);

            if(index <= m_fixedItems.size() - 1) {
                item->setFixed(true);
            } else {
                item->setRecordOrder(true);
            }
            //index为0表示第一个
            if(index == 0) {
                order = 0;
            } else if(index > 0){
                bool found = false;
                //寻找前一个item所在的位置，前一个item未注册时寻找再前一个的位置
                for(int preIndex = index - 1; preIndex >= 0 && !found; preIndex--) {
                    QString preId = m_order.at(preIndex);
                    for(int i = 0; i < m_item.size(); i++) {
                        if(m_item.at(i)->id() == preId) {
                            order = i + 1;
                            found = true;
                            break;
                        }
                    }
                }
                //找不到放到第一个
                if(!found) {
                    order = 0;
                }
            }
            break;
        }
    }
    beginInsertRows({}, order, order);
    m_item.insert(order, item);
    endInsertRows();
    Q_EMIT dataChanged(index(order, 0, {}), index(m_item.size() - 1, 0, {}), {Role::Row});
}

void TrayIconItemsModel::removeSource(const QString &source)
{
    int row = indexOfSource(source);
    if (row >= 0) {
        beginRemoveRows({}, row, row);
        m_item.removeAt(row);
        endRemoveRows();
    }
    Q_EMIT dataChanged(index(row + 1, 0, {}), index(m_item.size() - 2, 0, {}), {Role::Row});
}

void TrayIconItemsModel::dataUpdated(const QString &sourceName)
{
    int i = indexOfSource(sourceName);
    if (i >= 0) {
        Q_EMIT dataChanged(index(i, 0, {}), index(i, 0, {}));
    } else {
        addSource(sourceName);
    }
}

void TrayIconItemsModel::showMenu(QMenu *menu)
{
    menu->exec(QCursor::pos());
}

void TrayIconItemsModel::setOrder(const QModelIndex &index, int order)
{
    int oldRow = index.row();
    if ((oldRow < 0) || (oldRow >= m_item.length())) {
        return;
    }
    auto item = m_item.at(oldRow);
    if(item->fixed()) {
        return;
    }
    if(oldRow == order) {
        return;
    }

    QString id  = m_item.at(index.row())->id();
    int count = 0;
    if(order > oldRow) {
        if(item->recordOrder()) {
            QString idBefore;
            for(int row = order; row > oldRow; --row) {
                if(m_item.at(row)->recordOrder()) {
                    idBefore = m_item.at(row)->id();
                    break;
                }
            }
            if(!idBefore.isEmpty()) {
                int targetOrder = m_order.indexOf(idBefore);
                m_order.insert(targetOrder, m_order.takeAt(m_order.indexOf(id)));
                m_settings->setValue(QStringLiteral("orderedItems"), QStringList(m_order.mid(m_fixedItems.size())));
            }
        }
        beginMoveRows(index.parent(), oldRow, oldRow, index.parent(), order + 1);
        m_item.insert(order, m_item.takeAt(oldRow));
        endMoveRows();
        Q_EMIT dataChanged(this->index(oldRow, 0, {}), this->index(order, 0, {}), {Role::Row});
    } else {
        if(item->recordOrder()) {
            QString idAfter;
            for(int row = order; row < oldRow; ++row) {
                if(m_item.at(row)->recordOrder()) {
                    idAfter = m_item.at(row)->id();
                    break;
                }
            }
            if(!idAfter.isEmpty()) {
                int targetOrder = m_order.indexOf(idAfter);
                m_order.insert(targetOrder, m_order.takeAt(m_order.indexOf(id)));
                m_settings->setValue(QStringLiteral("orderedItems"), QStringList(m_order.mid(m_fixedItems.size())));
            }
        }
        beginMoveRows(index.parent(), oldRow, oldRow, index.parent(), order);
        m_item.insert(order, m_item.takeAt(oldRow));
        endMoveRows();
//        qDebug() << "order < oldRow===" << order << oldRow<< m_item;
        Q_EMIT dataChanged(this->index(order, 0, {}), this->index(oldRow, 0, {}), {Role::Row});
    }
}

void TrayIconItemsModel::activate(const QModelIndex &index)
{
    int oldRow = index.row();
    if ((oldRow < 0) || (oldRow >= m_item.length())) {
        return;
    }
    m_item.at(index.row())->activate(QCursor::pos().x(), QCursor::pos().y());
}

void TrayIconItemsModel::showContextMenu(const QModelIndex &index)
{
    int oldRow = index.row();
    if ((oldRow < 0) || (oldRow >= m_item.length())) {
        return;
    }
    m_item.at(index.row())->contextMenu(QCursor::pos().x(), QCursor::pos().y());
}
}
